package com.cjh.filter;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.cjh.constant.JwtConstant;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;

@Configuration
public class JwtTokenCheckFilter extends OncePerRequestFilter {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 每次请求都会进入这个方法
     * token从header带过来
     * 解析token
     * 设置到上下文里面去
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        response.setContentType("application/json;charset=utf-8");
        // 拿到url
        String path = request.getRequestURI();
        String method = request.getMethod();
        if ("/login".equals(path) && "POST".equalsIgnoreCase(method)) {
            // 直接放行 因为登录没有token
            filterChain.doFilter(request, response);
            return;
        }
        if (path.contains(".html")) {
            // 放行
            filterChain.doFilter(request, response);
            return;
        }
        if (path.contains("layui")) {
            // 放行
            filterChain.doFilter(request, response);
            return;
        }
        if (path.contains("js")) {
            // 放行
            filterChain.doFilter(request, response);
            return;
        }
        // 验证jwt
        String authorization = request.getHeader(JwtConstant.AUTHORIZATION);
        if (StringUtils.hasText(authorization)) {
            // 如果有 那么就拿到真正的token
            String jwt = authorization.replaceAll("bearer ", "");

            Boolean hasToken = redisTemplate.hasKey(JwtConstant.JWT_PREFIX + jwt);
            if (!hasToken){
                //redis没有token说明已经登出
                // 返回错误信息
                Map<String,Object> map=new HashMap<>();
                map.put("code",400);
                map.put("msg","token过期，请登录");
                PrintWriter writer = response.getWriter();
                writer.write(new ObjectMapper().writeValueAsString(map));
                writer.flush();
                writer.close();
                return;
            }
            // 解析jwt
            JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(JwtConstant.SIGN)).build();
            DecodedJWT decodedJWT = null;
            try {
                decodedJWT = jwtVerifier.verify(jwt);
            } catch (JWTVerificationException e) {
                // 报错了 写一个403出去
                response.getWriter().write("token验证失败");
                return;
            }
            // 拿到解析后的jwt了 后端服务器没保存用户信息 给他设置进去
            Claim usernameClaim = decodedJWT.getClaim("userName");
            String userName = usernameClaim.asString();
            // 拿到权限
            Claim authsClaim = decodedJWT.getClaim("auths");
            List<String> authStrs = authsClaim.asList(String.class);
            // 转变权限信息
            ArrayList<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>(authStrs.size() * 2);
            authStrs.forEach(auth -> simpleGrantedAuthorities.add(new SimpleGrantedAuthority(auth)));
            // 变成security认识的对象
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName, null, simpleGrantedAuthorities);
            // 怎么才能让security认识呢？
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            // 代码继续往下执行
            filterChain.doFilter(request, response);
            return;
        }
        //没有传递token
        Map<String,Object> map=new HashMap<>();
        map.put("code",400);
        map.put("msg","缺少token参数");
        PrintWriter writer = response.getWriter();
        writer.write(new ObjectMapper().writeValueAsString(map));
        writer.flush();
        writer.close();
        return;
    }
}
